package main

import (
	"fmt"
	"sync"
	"time"
)

/*
考场签到，名字丢入管道；
只有5个车道，最多供5个人同时考试；
考生按签到顺序依次考试，给予考生10%的违规几率；
每3秒钟巡视一次，发现违规的清出考场，否则输出考场时序良好；
所有考试者考完后，录入考试成绩；
考试结果写入MySQL,允许一写多读；
成绩录入完毕通知考生，考生查阅自己的成绩；
考生成绩丢入Redis缓存，成绩查询优先走缓存；
*/

var (
	//所有考生的签到手册
	chNames = make(chan string, 100)
	//chQueryNames = make(chan string, 100)

	names = make([]string, 0)

	//等待组（开辟协程时注册，结束一个子协程就注销一个，主协程等待至协程数注销干净为止）
	wg sync.WaitGroup


)

func main() {

	/*延时关闭MySQL和Redis（在当前函数返回前的最后一步执行）*/
	defer func() {
		CloseMysql()
		CloseRedis()
	}()

	/*生成一堆随机姓名，全部丢入管道chNames（签到簿）*/
	for i := 0; i < 20; i++ {
		name := GetRandomName()
		chNames <- name
	}
	//写入完毕关闭管道，遍历它的人才不会阻塞至死（它不确定你还会不会继续写入数据）
	close(chNames)

	/*为每个考生开辟一条考试协程*/
	for name := range chNames {
		//注册到等待组
		wg.Add(1)
		go func(_name string) {
			TakeExam(_name)
			wg.Done()
		}(name)
	}

	/*巡考人协程（没有注册到等待组，与主协程同寿）*/
	go Patrol()

	/*主协程阻塞等待至组内协程注销干净*/
	wg.Wait()
	fmt.Println("考试结束")

	/*录入成绩，遍历成绩单，对每个人的（姓名，成绩）插入数据库*/
	go InputScore2DB()
	//阻塞一秒以确保数据库写入协程InputScore2DB率先将数据库锁定为只写，这样读数据的人在数据没有完成录入之前就必须阻塞等待
	<-time.After(1 * time.Second)

	/*
	考生纷纷查询成绩，一人一条数据库读取协程
	数据库允许多读，无需控制并发数
	首次查询时，数据来自MySQL核心数据库
	后来来自Redis缓存（提高访问速度，减小核心数据库的压力）
	*/
	fmt.Println("第一轮查询成绩")
	for _, name := range names {
		wg.Add(1)
		go func(localName string) {
			QueryScore(localName)
			wg.Done()
		}(name)
	}
	//阻塞等待至第一轮成绩查询结束
	wg.Wait()

	/*第二轮查询数据主要来自缓存*/
	fmt.Println("\n第二轮查询成绩")
	for _, name := range names {
		wg.Add(1)
		go func(localName string) {
			QueryScore(localName)
			wg.Done()
		}(name)
	}
	wg.Wait()
	fmt.Println("成绩查询结束！")

}
